﻿using System;
using System.Collections.Generic;
using System.Linq;
using DNT.RavenQA.Core.Extensions;
using DNT.RavenQA.Core.Models;
using DNT.RavenQA.Web.Infrastructure.Indexing;
using DNT.RavenQA.Web.Infrastructure.QueryModel;
using Raven.Client;

namespace DNT.RavenQA.Web.ViewModels
{
    public class QuestionDetailsViewModel
    {
        public static QuestionDetailsViewModel Build(IDocumentSession session,
                                                     IParametrizedQuery<Answer, Question> query,
                                                     Int32 questionId,
                                                     User currentUser)
        {
            session.EnsureIsNotNull("session");
            query.EnsureIsNotNull("query");

            if (questionId <= 0)
                throw new ArgumentOutOfRangeException("questionId");

            var question = session.Include<Question>(x => x.User.Id)
                .Include<Question>(x => x.VotesId)
                .Include<Question>(x => x.ViewsId)
                .Load<Question>(questionId); // NOTE: It applyes conventions!

            if (question.IsNull())
                throw new NonExistingContributeException();

            var answers = query.Execute(session, question)
                .OrderByDescending(a => a.Accepted)
                .ThenBy(a => a.PostedOn)
                .ToList()
                .Select(a => AnswerSummary.Build(session, a)); // NOTE: inverting "Select" and "ToList" throws.

            return new QuestionDetailsViewModel
                       {
                           Question = QuestionDetails.Build(session,
                                                            question, 
                                                            currentUser.IsNull() ? null : UserInfo.Build(currentUser)),
                           Answers = answers
                       };
        }

        public IEnumerable<AnswerSummary> Answers { get; private set; }

        public QuestionDetails Question { get; private set; }

        public AnswerInput Input
        {
            get { return _input; }
            set { _input = value; }
        }
        private AnswerInput _input = new AnswerInput();

        public class QuestionDetails
        {
            public static QuestionDetails Build(IDocumentSession session, Question question, UserInfo currentUser)
            {
                session.EnsureIsNotNull("session");
                question.EnsureIsNotNull("question");

                var votes = question.VotesId.IsNull() 
                    ? null 
                    : session.Load<ContributeVotes>(question.VotesId);
                var views = question.ViewsId.IsNull() 
                    ? null 
                    : session.Load<ContributeViews>(question.ViewsId);
                var featuredTags = new FeaturedTagsQuery(question)
                    .Execute(session)
                    .ToList();

                return new QuestionDetails
                           {
                               Id = question.Key,
                               Body = question.Body,
                               Title = question.Title,
                               PostedOn = question.PostedOn,
                               Tags = new List<String>(question.Tags),
                               Author = UserInfo.Build(session, question),
                               Score = votes.IsNull() ? 0 : votes.TotalAmount,
                               Views = views.IsNull() ? 0 : views.TotalAmount,
                               FeaturedTags = featuredTags,
                               CurrentUser = currentUser,
                               Answered = question.Answered
                           };
            }

            protected QuestionDetails() { }

            public Int32 Id { get; private set; }
            public String Title { get; private set; }
            public String Body { get; private set; }
            public DateTimeOffset PostedOn { get; private set; }
            public IEnumerable<String> Tags { get; private set; }
            public UserInfo Author { get; private set; }
            public Int32 Score { get; private set; }
            public Int32 Views { get; private set; }
            public IEnumerable<QuestionsTagListIndex.ReduceResult> FeaturedTags { get; private set; }

            public UserInfo CurrentUser { get; private set; }

            public Boolean IsOwnedByCurrentUser
            {
                get { return CurrentUser.IsNotNull() && Author == CurrentUser; }
            }

            public Boolean Answered { get; private set; }
        }

        public class AnswerSummary
        {
            public static AnswerSummary Build(IDocumentSession session, Answer answer)
            {
                session.EnsureIsNotNull("session");
                answer.EnsureIsNotNull("answer");

                var votes = answer.VotesId.IsNull() 
                    ? null 
                    : session.Load<ContributeVotes>(answer.VotesId);

                return new AnswerSummary
                           {
                               AnswerId = answer.Key,
                               Content = answer.Body,
                               PostedOn = answer.PostedOn,
                               Author = UserInfo.Build(session, answer),
                               Score = votes.IsNull() ? 0 : votes.TotalAmount,
                               Accepted = answer.Accepted
                           };
            }

            public Int32 Score { get; private set; }
            public Int32 AnswerId { get; private set; }
            public String Content { get; private set; }
            public DateTimeOffset PostedOn { get; private set; }
            public UserInfo Author { get; private set; }
            public Boolean Accepted { get; private set; }
        }
    }
}